﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Input;

namespace Kinixt
{
    class Robot
    {
        private Vector3 position;
        private Vector3 direction;
        private Vector3 up;
        private Vector3 velocity;
        private Vector3 right;
        private Matrix world;
        private const float MINIMUM_ALTITUDE = 20.0f;
        private const float ROTATION_RATE = 1.0f;
        private const float MASS = 3.0f;
        private const float THRUST_FORCE = 24000.0f;
        private const float DRAG_FACTOR = 0.97f;
        private const float WHEEL_ROTATON_VALUE_SCALE = 5.0f;

        private Vector2 rotationAmount;
        private float thrustAmount;

        Model tankModel;
        ModelBone leftBackWheelBone;
        ModelBone rightBackWheelBone;
        ModelBone leftFrontWheelBone;
        ModelBone rightFrontWheelBone;
        ModelBone turretBone;
        ModelBone cannonBone;

        Matrix leftBackWheelTransform;
        Matrix rightBackWheelTransform;
        Matrix leftFrontWheelTransform;
        Matrix rightFrontWheelTransform;
        Matrix turretTransform;
        Matrix cannonTransform;

        Matrix[] boneTransforms;

        private float wheelRotationValue;
        private const float turretRotationValue = 1.6f;
        private float cannonRotationValue;


        #region Properties

        public Vector3 Position
        {
            get { return position; }
            set { position = value; }
        }

        public Vector3 Direction
        {
            get { return direction; }
            set { direction = value; }
        }

        public float ThrustAmount
        {
            get { return thrustAmount; }
            set { thrustAmount = value; }
        }

        public Vector2 RotationAmount
        {
            get { return rotationAmount; }
            set { rotationAmount = value; }
        }


        public float CannonRotation
        {
            get { return cannonRotationValue; }
            set { cannonRotationValue = value; }
        }

        #endregion

        /// <summary>
        /// Initiates robot
        /// </summary>
        public Robot()
        {
            rotationAmount = new Vector2(0, 0);
            ResetPosition();
        }

        /// <summary>
        /// Reset position of robot relative to wolrd
        /// </summary>
        public void ResetPosition()
        {
            Position = new Vector3(0, MINIMUM_ALTITUDE, 0);
            Direction = Vector3.Forward;
            up = Vector3.Up;
            right = Vector3.Right;
            velocity = Vector3.Zero;
        }

        /// <summary>
        /// Load model of robot
        /// </summary>
        /// <param name="content">content manager containing robot model</param>
        public void Load(ContentManager content)
        {
            tankModel = content.Load<Model>("tank");

            leftBackWheelBone = tankModel.Bones["l_back_wheel_geo"];
            rightBackWheelBone = tankModel.Bones["r_back_wheel_geo"];
            leftFrontWheelBone = tankModel.Bones["l_front_wheel_geo"];
            rightFrontWheelBone = tankModel.Bones["r_front_wheel_geo"];
            turretBone = tankModel.Bones["turret_geo"];
            cannonBone = tankModel.Bones["canon_geo"];

            leftBackWheelTransform = leftBackWheelBone.Transform;
            rightBackWheelTransform = rightBackWheelBone.Transform;
            leftFrontWheelTransform = leftFrontWheelBone.Transform;
            rightFrontWheelTransform = rightFrontWheelBone.Transform;
            turretTransform = turretBone.Transform;
            cannonTransform = cannonBone.Transform;

            boneTransforms = new Matrix[tankModel.Bones.Count];
        }

        /// <summary>
        /// Draw model of robot
        /// </summary>
        /// <param name="camera">Camera object currently used</param>
        public void Draw(BehindHeadCamera camera)
        {
            tankModel.Root.Transform = this.world;

            Matrix wheelRotation = Matrix.CreateRotationX(wheelRotationValue);
            Matrix turretRotation = Matrix.CreateRotationY(turretRotationValue);
            Matrix cannonRotation = Matrix.CreateRotationX(cannonRotationValue);

            leftBackWheelBone.Transform = wheelRotation * leftBackWheelTransform;
            rightBackWheelBone.Transform = wheelRotation * rightBackWheelTransform;
            leftFrontWheelBone.Transform = wheelRotation * leftFrontWheelTransform;
            rightFrontWheelBone.Transform = wheelRotation * rightFrontWheelTransform;
            turretBone.Transform = turretRotation * turretTransform;
            cannonBone.Transform = cannonRotation * cannonTransform;

            tankModel.CopyAbsoluteBoneTransformsTo(boneTransforms);

            foreach (ModelMesh mesh in tankModel.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.EnableDefaultLighting();
                    effect.World = boneTransforms[mesh.ParentBone.Index];
                    effect.View = camera.View;
                    effect.Projection = camera.Projection;                   
                }
                mesh.Draw();
            }
        }

        /// <summary>
        /// Update robots properties such as position or direction 
        /// </summary>
        /// <param name="gameTime">Current time</param>
        public void Update(GameTime gameTime)
        {
            KeyboardState keyboardState = Keyboard.GetState();
            MouseState mouseState = Mouse.GetState();

            float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;
            float time = (float)gameTime.TotalGameTime.TotalSeconds;


            //this.turretRotation = 1.6f;
            GamePadState gamePadState = GamePad.GetState(PlayerIndex.One);
            Vector2 RotationAmountTMP = -gamePadState.ThumbSticks.Left;

            // Scale rotation amount to radians per second
            RotationAmountTMP = RotationAmount * ROTATION_RATE * elapsed;


            // Create rotation matrix from rotation amount
            Matrix rotationMatrix = Matrix.CreateRotationY(RotationAmountTMP.X);


            // Rotate orientation vectors
            Direction = Vector3.TransformNormal(Direction, rotationMatrix);
            up = Vector3.TransformNormal(up, rotationMatrix);

            Direction.Normalize();
            up.Normalize();


            right = Vector3.Cross(Direction, up);
            up = Vector3.Cross(right, Direction);


            float ThrustAmountTMP = gamePadState.Triggers.Right;
            ThrustAmountTMP = thrustAmount;


            if (ThrustAmountTMP > 0.0)
            {
                this.wheelRotationValue = time * -WHEEL_ROTATON_VALUE_SCALE;
            }
            else if (ThrustAmountTMP < 0.0)
            {
                this.wheelRotationValue = time * WHEEL_ROTATON_VALUE_SCALE;
            }
            else
            {
                this.wheelRotationValue = ThrustAmountTMP;
            }


            // do simple physics
            Vector3 force = Direction * ThrustAmountTMP * THRUST_FORCE;
            Vector3 acceleration = force / MASS;
            velocity += acceleration * elapsed;
            velocity *= DRAG_FACTOR;
            Position += velocity * elapsed;
            position.Y = Math.Max(Position.Y, MINIMUM_ALTITUDE);


            // update tanks world variables
            world = Matrix.Identity;
            world.Forward = Direction;
            world.Up = up;
            world.Right = right;
            world.Translation = Position;
        }

    }
}
